import L from 'leaflet'
import { getMapPointDetail } from './api'
import { EventManager } from './event-manager'
import mapConfigObj from './mapConfig'
import { ElMessage, ElMessageBox } from 'element-plus'

interface AreaNameConfig {
  lat: number
  lng: number
  name: string
  children: AreaNameConfig[]
}

interface PointConfig {
  lat: number
  lng: number
  pointId: number
  name: string
  icon: string
}
interface Vector {
  x: number
  y: number
}
export interface GuideUIItem {
  lat: number
  lng: number
  icon: string
  angle: number
}

export class MapManager {
  private map: L.Map
  private areaNameLayerGroup: L.LayerGroup | undefined
  private areaNameMarker: L.Marker | undefined
  private pointLayerGroup: L.LayerGroup | undefined
  private prevZoom = 0
  private mapAnchorList: AreaNameConfig[] = []
  private pointList: PointConfig[] = []
  private lastActivePointId = -1
  private mapConfig: any = {}

  constructor(domId: string, mapId: string) {
    this.mapConfig = mapConfigObj[mapId]
    const bounds = L.latLngBounds(
      L.latLng(this.mapConfig.bounds[0][0], this.mapConfig.bounds[0][1]),
      L.latLng(this.mapConfig.bounds[1][0], this.mapConfig.bounds[1][1]),
    )
    this.map = L.map(domId, {
      center: this.mapConfig.center,
      maxBounds: bounds,
      crs: L.CRS.Simple,
      attributionControl: false,
      zoomControl: false,
      maxZoom: this.mapConfig['maxZoom'],
      minZoom: this.mapConfig['minZoom'],
      zoom: 5,
    })
    this.prevZoom = this.map.getZoom()
    this.map.addControl(
      // @ts-ignore
      new L.Control.Zoomslider({ position: 'bottomright', stepHeight: 30, knobHeight: 20 }),
    )
    L.tileLayer('/images/map/{id}/{z}/{x}/{y}.' + this.mapConfig.mapType, {
      id: this.mapConfig['mapName'],
      maxZoom: this.mapConfig['maxZoom'],
      minZoom: this.mapConfig['minZoom'],
      bounds,
      noWrap: true,
    }).addTo(this.map)
    //不同zoom展示不同地区的name
    this.map.on('zoom', () => {
      const prevRenderFlag = this.prevZoom >= 6
      const curRenderFlag = this.map.getZoom() >= 6
      if (prevRenderFlag !== curRenderFlag) {
        this.renderAreaNames()
      }

      this.prevZoom = this.map.getZoom()
    })
    this.map.on('click', this.onMapClick.bind(this))
    this.map.on('moveend', this.onMapMoveEnd.bind(this))
  }
  onMapClick() {
    const lastActivePoint = document.getElementById(`mapPointItem${this.lastActivePointId}`)
    lastActivePoint?.classList.remove('active')
    this.lastActivePointId = -1
  }

  onMapMoveEnd() {
    this.calcOutScreenPoints()
  }
  setMapAnchorList(configList: AreaNameConfig[]) {
    this.mapAnchorList = configList
  }
  renderAreaNames() {
    this.areaNameLayerGroup?.clearLayers()
    let markers: L.Marker[] = []
    if (this.map.getZoom() >= 6) {
      this.mapAnchorList.forEach((val) => {
        let childrenList: L.Marker[] = []
        childrenList = val.children.map(this.getAreaNameMarkerItem)
        markers = markers.concat(childrenList)
      })
    } else {
      markers = this.mapAnchorList.map(this.getAreaNameMarkerItem)
    }
    this.areaNameLayerGroup = L.layerGroup(markers)
    this.areaNameLayerGroup.addTo(this.map)
  }
  caclEditPopupContent(name: string, latlng: L.LatLng) {
    return `<div class="edit-popup-container">
                <div class="edit-title">拖拽point改变点位置</div>
                <hr/>
                <div class="edit-latlng">经纬度：
                  <div>${formartLatlng(latlng.lat)}，${formartLatlng(latlng.lng)}</div>
                </div>
                <div class="edit-body">
                    <input type="text" class="edit-input" id="save-input"value="${name}"></input>
                    <button id="save-button">保存</button>
                </div>
            </div>`
  }
  handleAreaNames(name: string, latlng: L.LatLng) {
    this.areaNameMarker?.remove()
    this.areaNameMarker = L.marker(latlng, {
      draggable: true,
    }).addTo(this.map)
    const popup = L.popup({
      autoClose: false,
      closeOnClick: false,
    }).setContent(this.caclEditPopupContent(name, latlng))

    this.areaNameMarker.bindPopup(popup).openPopup()
    const handleClickEditButton = () => {
      const input = document.getElementById('save-input')
      if (!input.value) {
        ElMessageBox.alert('地区名称不能为空', '警告', {
          confirmButtonText: 'OK',
        })
      } else {
        EventManager.emit('SavePointsEdit', this.areaNameMarker?.getLatLng(), input.value)
        this.areaNameMarker?.remove()
      }
    }
    const button = document.getElementById('save-button')
    if (button) {
      button.removeEventListener('click', handleClickEditButton)
      button.addEventListener('click', handleClickEditButton)
    }
    this.areaNameMarker.on('popupopen', function () {
      const button = document.getElementById('save-button')
      if (button) {
        button.removeEventListener('click', handleClickEditButton)
        button.addEventListener('click', handleClickEditButton)
      }
    })

    this.areaNameMarker.on('dragend', (event: L.DragEndEvent) => {
      popup
        .setContent(this.caclEditPopupContent(name, this.areaNameMarker.getLatLng()))
        .openOn(this.map)
    })
  }

  getAreaNameMarkerItem(config: AreaNameConfig) {
    const { lat = 0, lng = 0, name } = config
    return L.marker(L.latLng(lat, lng), {
      icon: L.divIcon({
        className: 'map-mark-item',
        html: `<div class="area-mark-item">${name}</div>`,
      }),
    })
  }
  renderPoints(pointList: PointConfig[]) {
    this.pointList = pointList
    this.pointLayerGroup?.clearLayers()
    const pointerMarkers = pointList.map((val) => {
      const { lat, lng, icon, pointId, name } = val
      const marker = L.marker(L.latLng(lat, lng), {
        icon: L.divIcon({
          className: 'map-point-item',
          html: `<div class="point-item-container ${this.mapConfig.pointClass}" id="mapPointItem${pointId}">
            <div class="point-pic" style="background-image: url(${icon});"></div>
            <div class="arrow-icon lt"></div>
            <div class="arrow-icon lb"></div>
            <div class="arrow-icon rb"></div>
            <div class="arrow-icon rt"></div>
          </div>`,
          iconSize: [37, 40],
          iconAnchor: [19, 20],
        }),
      })

      marker.bindPopup(
        L.popup({
          content: this.calcPopupContent({
            info: {},
            correct_user_list: [],
            last_update_time: '',
            name: '',
          }),
        }),
      )

      marker.on('popupopen', async () => {
        const res = await getMapPointDetail(pointId)
        const popupData = { ...res.data, name }
        marker.setPopupContent(this.calcPopupContent(popupData))
      })

      marker.on('click', () => {
        if (this.lastActivePointId === pointId) return
        const lastActivePoint = document.getElementById(`mapPointItem${this.lastActivePointId}`)
        lastActivePoint?.classList.remove('active')

        const curPoint = document.getElementById(`mapPointItem${pointId}`)
        curPoint?.classList.add('active')

        this.lastActivePointId = pointId
      })
      return marker
    })
    this.pointLayerGroup = L.featureGroup(pointerMarkers)

    this.pointLayerGroup = L.layerGroup(pointerMarkers).addTo(this.map)
    this.calcOutScreenPoints()
  }
  enableClickDebug() {
    this.map.on('click', (workingLayer) => {
      const cordinate = workingLayer.latlng
      console.log('cordinate', cordinate)
    })
  }
  flyTo(latlng: L.LatLngExpression, zoom?: number) {
    this.map.flyTo(latlng, zoom)
  }
  calcPopupContent(popupData: any) {
    const { correct_user_list, info, last_update_time, name } = popupData
    const avatarElmStr = correct_user_list.map((val: any) => {
      return `<div class="avatar-item" style="background-image: url(${val.img})"></div>`
    })
    return `<div class="point-popup-container">
    <div class="popup-title">${name}</div>
    <div class="popup-pic" style="background-image: url(${info.img})"></div>
    <div class="point-name">${info.content}</div>
    <div class="contributor-container">
      <div class="contributor-label">贡献者：</div>
      <div class="avatar-container">
        ${avatarElmStr}
      </div>
    </div>
    <div class="point-time">更新时间：${last_update_time}</div>
  </div>`
  }
  calcOutScreenPoints() {
    const guideUIAry: GuideUIItem[] = []
    const calcPointMap: { [key: string]: any } = {}
    const center = this.map.getCenter()
    for (let i = 0; i < this.pointList.length; i++) {
      const pointItem = this.pointList[i]
      const { name } = pointItem
      if (!calcPointMap[name]) {
        calcPointMap[name] = {}
      }

      if (calcPointMap[name].inScreen) {
        continue
      }

      const isContain = this.map.getBounds().contains(pointItem)
      if (!isContain) {
        const dist = center.distanceTo(pointItem)
        if (!calcPointMap[name].pointItem) {
          calcPointMap[name] = { dist, pointItem, inScreen: false }
        } else {
          const curDist = calcPointMap[name].dist
          if (dist < curDist) {
            calcPointMap[name] = { dist, pointItem, inScreen: false }
          }
        }
      } else {
        calcPointMap[name].inScreen = true
      }
    }

    for (const key in calcPointMap) {
      const { inScreen, pointItem } = calcPointMap[key]
      if (!inScreen) {
        const { lat, lng, icon } = pointItem
        const directionVector = { x: lng - center.lng, y: lat - center.lat }
        const xVector = { x: 1, y: 0 }
        const angle = calcVectorAngle(xVector, directionVector)
        guideUIAry.push({ angle, icon, lat, lng })
      }
    }
    EventManager.emit('RenderMapGuideUI', guideUIAry)
  }
}
function calcVectorAngle(vectorA: Vector, vectorB: Vector) {
  const dotProduct = vectorA.x * vectorB.x + vectorA.y * vectorB.y
  const magnitudeA = Math.sqrt(vectorA.x * vectorA.x + vectorA.y * vectorA.y)
  const magnitudeB = Math.sqrt(vectorB.x * vectorB.x + vectorB.y * vectorB.y)

  const cosTheta = dotProduct / (magnitudeA * magnitudeB)
  const theta = Math.acos(cosTheta)

  const crossProduct = vectorA.x * vectorB.y - vectorA.y * vectorB.x
  const direction = crossProduct > 0 ? 1 : -1

  return direction * theta
}
function formartLatlng(n: number) {
  const a = String(n).split('.')[1]
  if (a?.length > 5) {
    return n.toFixed(5)
  } else {
    return n
  }
}
